package com.ruoyi.biz.util;

import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Arrays;

/**
 * 独立部署的激活码生成与验证工具
 *
 * @author yusheng.liu
 * @since 2025/11/10 14:01
 */
public class GenCodeUtil {

    /**
     * 独立部署的激活码生成与验证工具
     * 生成格式如：ABCD-EFGH-IJKL-MNOP
     */
    // 私有盐值（建议修改为你自己的随机字符串，用于防伪造）
    private static final String SALT = "Lys1qaz@wsx2025!@#";

    // 激活码长度（不含分隔符），必须是4的倍数，推荐16
    private static final int CODE_LENGTH = 16;

    // 分组大小（每4个字符一组）
    private static final int GROUP_SIZE = 4;

    private static final SecureRandom random = new SecureRandom();

    /**
     * 生成一个全局唯一的激活码
     *
     * @return 格式如 ABCD-EFGH-IJKL-MNOP 的激活码
     */
    public static String generateActivationCode() {
        long timestamp = Instant.now().toEpochMilli(); // 当前时间戳
        byte[] randomBytes = new byte[8];
        random.nextBytes(randomBytes);

        // 构造原始数据：时间戳 + 随机字节 + 盐值
        String rawData = timestamp + Arrays.toString(randomBytes) + SALT;

        // 使用 SHA-256 哈希
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(rawData.getBytes());

            // 取前 N 字节（N = CODE_LENGTH * 5 / 8，因为 Base32 每5位编码1字符）
            int byteCount = (CODE_LENGTH * 5 + 7) / 8; // 向上取整
            byte[] selected = Arrays.copyOf(hash, Math.min(byteCount, hash.length));

            // Base32 编码（无填充、大写、去掉了易混淆字符如 0/O, 1/I/L）
            String base32 = encodeBase32(selected).substring(0, CODE_LENGTH).toUpperCase();

            // 插入连字符
            return formatWithHyphens(base32);
        } catch (Exception e) {
            throw new RuntimeException("生成激活码失败", e);
        }
    }

    /**
     * 验证激活码是否由本系统生成（防伪造）
     *
     * @param code 要验证的激活码（如 ABCD-EFGH-IJKL-MNOP）
     * @return 是否有效
     */
    public static boolean validateActivationCode(String code) {
        if (code == null || code.trim().isEmpty()) return false;
        String clean = code.replace("-", "").toUpperCase();
        if (clean.length() != CODE_LENGTH) return false;

        // 尝试重新生成所有可能？——不行，因为用了随机数。
        // 所以我们无法“逆向验证”内容，但可以校验格式和字符集。
        // 更强的验证需保存已发码（但违背“独立部署无存储”原则）

        // 因此，本方案的验证仅做格式和字符合法性检查
        // 若需严格验证，建议记录已发放的激活码（如写入文件或内存 Set）
        return isValidBase32String(clean);
    }

    // ==================== 辅助方法 ====================

    /**
     * 自定义 Base32 编码（兼容 RFC 4648，但去除 0/O/1/I/L 等易混淆字符）
     */
    private static final char[] BASE32_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789".toCharArray(); // 32个字符，去掉 I, L, O, 0, 1

    private static String encodeBase32(byte[] data) {
        StringBuilder sb = new StringBuilder();
        long value = 0;
        int bits = 0;

        for (byte b : data) {
            value <<= 8;
            value |= b & 0xFF;
            bits += 8;
            while (bits >= 5) {
                bits -= 5;
                int index = (int) ((value >> bits) & 0x1F);
                sb.append(BASE32_CHARS[index]);
            }
        }
        if (bits > 0) {
            value <<= (5 - bits);
            sb.append(BASE32_CHARS[(int) (value & 0x1F)]);
        }
        return sb.toString();
    }

    private static boolean isValidBase32String(String s) {
        for (char c : s.toCharArray()) {
            if (Arrays.binarySearch(BASE32_CHARS, c) < 0) {
                return false;
            }
        }
        return true;
    }

    private static String formatWithHyphens(String raw) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < raw.length(); i += GROUP_SIZE) {
            if (i > 0) sb.append('-');
            sb.append(raw, i, Math.min(i + GROUP_SIZE, raw.length()));
        }
        return sb.toString();
    }

    // ==================== 测试用例 ====================
//
//    public static void main(String[] args) {
//        System.out.println("生成激活码示例：");
//        for (int i = 0; i < 5; i++) {
//            String code = generateActivationCode();
//            System.out.println(code + " -> " + validateActivationCode(code));
//        }
//
//        // 测试无效码
//        System.out.println("\n无效码测试：");
//        System.out.println(validateActivationCode("ABCD-EFGH-IJKL-MNOO")); // 含 O
//        System.out.println(validateActivationCode("ABCD-EFGH-IJKL"));      // 长度不对
//    }

}
